在Spring之前的一些版本中,用户需要在web应用程序上下文中定义一个或多个HandlerMapping
将进来的web请求映射到合适的处理器。在引入了被注解的控制器后,你不再需要这么做,因为RequestMappingHandlerMapping
会自动在所有的@Controller
bean上查找@ReqeuestMapping
。然而,请机组所有的HandlerMapping
类都继承自AbstractHandlerMapping
,都有下列的属性,你可以利用这些属性来定制它们的行为:
interceptors
:使用的拦截器的列表。HandlerInterceptors
会在22.4.1节,"Intercepting requests with a HandlerInterceptor"中讨论。
defaultHandler
:默认使用的处理器,当不存在匹配的处理器时,会使用默认的处理器。
order
:取决于order属性(见org.springframework.core.Order
接口),Spring会将上下文中所有可用的处理器排序,并应用第一个符合的处理器。
alwaysUseFullPath
:如果为true
,Spring使用当前Servlet上下文中的完整路径来查找合适的处理程序。如果是false
(默认值),当前Servlet上下文中映射的路径会被使用。比如,如果一个Servlet映射到/testing/*
,并且alwaysUseFullPath
属性被设为true,那么/testing/viewPage.html
会被使用,相反的如果这个属性被设为false,那么/viewPage.html
会被使用。
* urlDecode
:从Spring2.5开始,默认值为true
。如果你想要比较编码路径,将其设置为false
。然而,HttpServletRequest
总是会以解码的形式暴露出Servlet路径。注意比较编码的路径时,Servlet路径可能不匹配。
下面的例子展示了如何配置一个拦截器:
<beans>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<bean class="example.MyInterceptor"/>
</property>
</bean>
<beans>
Spring的处理器包括拦截器的映射机制,在你想要自动引用某些特定的功能到一些请求时十分有用,比如,某个重要的检查。
拦截器必须要实现org.springframework.web.servlet
包中的HandlerInterceptor
接口。这个接口定义了三个方法:preHandle(..)
会在实际的处理器执行前执行;postHandle(..)
会在处理器执行之后执行;afterCompetion(..)
会在请求被完整的处理完之后执行。这三种方法为所有的前置处理和后置处理提供了足够的灵活性。
preHandle(..)
方法返回了一个boolean值。你可以利用这个方法去中断或是继续执行链。当一个方法返回true
时,处理器执行链会继续;当这个方法返回false时,DispatcherServlet
会认为拦截器自己完成了请求的处理(比如,渲染了一个合适的视图),不会再执行执行链中的其他的拦截器和实际的处理器。
拦截器可以使用interceptors
属性配置,这个属性存在于所有继承自AbstractHandlerMapping
的HandlerMapping
类中。下面是一个例子:
<beans>
<bean id="handlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="interceptors">
<list>
<ref bean="officeHoursInterceptor"/>
</list>
</property>
</bean>
<bean id="officeHoursInterceptor"
class="samples.TimeBasedAccessInterceptor">
<property name="openingTime" value="9"/>
<property name="closingTime" value="18"/>
</bean>
</beans>
package samples;
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
private int openingTime;
private int closingTime;
public void setOpeningTime(int openingTime) {
this.openingTime = openingTime;
}
public void setClosingTime(int closingTime) {
this.closingTime = closingTime;
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
Calendar cal = Calendar.getInstance();
int hour = cal.get(HOUR_OF_DAY);
if (openingTime <= hour && hour < closingTime) {
return true;
}
response.sendRedirect("http://host.com/outsideOfficeHours.html");
return false;
}
}
这个映射处理的任何请求都会被TimeBasedAccessInterceptor
拦截。如果当前时间不在工作时间内,用户会被重定向到静态的页面,比如,显示你只能在官方时间内访问官网。
当使用
ReqeustMappingHandlerMapping
时,实际的处理器是HandlerMethod
的一个实例,它定义了具体调用的控制器方法。
正如你所见的,Spring适配器类HandlerInterceptorAdapter
让继承HandlerInterceptor
接口变得简单。
在上面的例子中,配置的拦截器会自动的应用到所有的被注解的控制器方法上。如果你希望缩小拦截器应道的URL路径,你可以使用MVC命名空间或是MVC Java配置,或是声明一个
MappedInterceptor
类型的实例。见22.16.1节,"Enabling the MVC Java Config or the MVC XML Namespace"。
注意HandlerInterceptor
的postHandle
方法并不总是合适@ResponseBody
和ResponseEntity
的方法。在这种情况下,HttpMessageConverter
会在postHandle
调用前写入和提交相响应,这会呆滞无法修改响应,比如添加一个响应头。可以让应用程序实现ResponseBodyAdvice
或是为它声明一个@COntrollerAdvice
,在或者就是直接在RequestMappingHandlerAdapter
中配置。